#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "dtk_types.h"
#include "dtk_property.h"

DTKPropertyManager* dtk_propman_create ()
{
    DTKPropertyManager* pm;

    pm = (DTKPropertyManager*)malloc (sizeof(DTKPropertyManager));
    memset (pm, 0, sizeof(DTKPropertyManager));

    return pm;
}

void dtk_propman_release (DTKPropertyManager* self)
{
    if (self == NULL)
    {
        return;
    }

    if (self->props)
    {
        DTKProperty* prop, *next;

        prop = self->props;
        while (prop)
        {
            next = prop->next;
            free (prop);
            prop = next;
        }
    }

    free (self);

    return;
}

static DTKProperty* prop_create (DString key, DTKAccessProp access, DUInt type,
    DTKSetProperty set, DTKGetProperty get)
{
    DTKProperty* prop;

    prop = (DTKProperty*)malloc(sizeof(DTKProperty));
    prop = memset (prop, 0, sizeof(DTKProperty));
    prop->key = strdup(key);
    prop->type = type;
    prop->access = access;
    if (access & DPP_WRONLY)
    {
        prop->set_property = set;
    }
    if (access & DPP_WRONLY)
    {
        prop->get_property = get;
    }

    return prop;
}

void dtk_prop_register(DTKPropertyManager* self,
    DString key, DTKAccessProp access, DUInt type,
    DTKSetProperty set, DTKGetProperty get)
{
    DTKProperty *prop, *new_prop, *prev_prop;
    DInt ret;
    if (self == NULL || key == NULL || access == DPP_NORMAL)
    {
        return;
    }

    prop = self->props;

    while (prop)
    {
        ret = strcmp (prop->key, key);

        if (ret == 0)
        {
            prop->access = access;
            if (access & DPP_RDONLY)
            {
                prop->get_property = get;
            }
            if (access & DPP_WRONLY)
            {
                prop->set_property = set;
            }
            return;
        }
        
        if (ret > 0)
        {
            new_prop = prop_create(key, access, type, set, get);
            new_prop->next = prop;            
            if (prop->prev)
            {
                new_prop->prev = prop->prev;
                prop->prev->next = new_prop;                
            }
            prop->prev = new_prop;
            self->total ++;
            return;
        }
        prev_prop = prop;
        prop = prop->next;
    }

    if (prop == NULL)
    {
        new_prop = prop_create(key, access, type, set, get);
        if (self->props == NULL)
        {
            self->props = new_prop;
            self->total ++;
        }
        else if (prev_prop != NULL)
        {
            new_prop->prev = prev_prop;
            prev_prop->next = new_prop;
            self->total ++;
        }
    }
    
    return;
}

static DTKProperty* prop_find (DTKPropertyManager* self, DString key)
{
    DTKProperty* prop;

    if (self->total <= 0)
    {
        return NULL;
    }

    prop = self->props;

    while (prop)
    {
        if (!strcmp (prop->key, key))
        {
            return prop;
        }
        prop = prop->next;
    }

    return NULL;
}

void dtk_prop_set(DTKPropertyManager* self, DTKActor* actor,
    DString key, DValue* value)
{
    DTKProperty* prop;

    if (self == NULL || key == NULL || value == NULL)
    {
        return;
    }

    prop = prop_find (self, key);

    if (prop == NULL || prop->type != value->type || !(prop->access & DPP_WRONLY))
    {
        return;
    }

    if (prop->set_property != NULL)
    {
        prop->set_property(actor, value, NULL);
    }

    return;
}

void dtk_prop_get(DTKPropertyManager* self, DTKActor* actor,
    DString key, DValue* value)
{
    DTKProperty* prop;

    if (self == NULL || key == NULL || value == NULL)
    {
        return;
    }

    prop = prop_find (self, key);

    if (prop == NULL || !(prop->access & DPP_RDONLY))
    {
        return;
    }

    if (prop->get_property != NULL)
    {
        prop->get_property(actor, value);
    }

    return;
}